package com.zerro.litez.compiler.processor;

import com.zerro.litez.annotations.dao.Dao;
import com.zerro.litez.annotations.entity.Entity;
import com.zerro.litez.annotations.entity.SupportedEnum;
import com.zerro.litez.compiler.processor.file_builder.*;
import com.zerro.litez.compiler.processor.parser.DaoClassParser;
import com.zerro.litez.compiler.processor.parser.EntityClassParser;
import com.zerro.litez.compiler.processor.parser.EnumParser;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Set;

public class LitEzProcessor extends AbstractProcessor {


    private final Set<Class<? extends Annotation>> annotationSet = new HashSet<>();


    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        annotationSet.add(Entity.class);
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {

        if (set.isEmpty()){
            return false;
        }

        FileBuilder.setFiler(processingEnv.getFiler());

        sendElementsAnnotatedWith(roundEnvironment, SupportedEnum.class, new SendTo() {
            @Override
            public boolean isTheType(TypeElement element) {
                if (element.getKind() == ElementKind.ENUM) {
                    EnumParser parser = new EnumParser(element);
                    return true;
                }
                return false;
            }

            @Override
            public void notTheType(TypeElement element) {
                throw new RuntimeException("@SupportedEnum 注解必须在 Enum 上声明, <"
                        + element + "> 不是 Enum.");
            }
        });


        sendElementsAnnotatedWith(roundEnvironment, Entity.class, new SendTo() {
            @Override
            public boolean isTheType(TypeElement element) {
                if (element.getKind().isClass()) {
                    new EntityClassParser(element);
                    return true;

                }
                return false;
            }

            @Override
            public void notTheType(TypeElement element) {
                throw new RuntimeException("@Entity 注解必须在 Class 上声明, <"
                        + element + "> 不是 Class.");
            }
        });


        for (EntityClassParser parser : EntityClassParser.getEntityClassParserMap().values()) {
            parser.onParse();
        }

        for (EntityClassParser parser : EntityClassParser.getEntityClassParserMap().values()) {
            new FormatterClassBuilder(parser, parser.getParsingElement());
        }



        sendElementsAnnotatedWith(roundEnvironment, Dao.class, new SendTo() {
            @Override
            public boolean isTheType(TypeElement element) {
                if (element.getKind().isInterface()) {
                    DaoClassParser parser = new DaoClassParser(element);
                    new DaoImpBuilder(parser, element);
                    return true;
                }
                return false;
            }

            @Override
            public void notTheType(TypeElement element) {
                throw new RuntimeException("@Dao 注解必须在 interface 上声明, <"
                        + element + "> 不是 interface.");
            }

        });
        DatabaseImpBuilder builder = new DatabaseImpBuilder();

        return false;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> set = new HashSet<>();
        for (Class<? extends Annotation> aClass : annotationSet) {
            set.add(aClass.getCanonicalName());
        }
        return set;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }


    public interface SendTo {
        boolean isTheType(TypeElement element);

        void notTheType(TypeElement element);
    }

    public void sendElementsAnnotatedWith(RoundEnvironment environment, Class<? extends Annotation> annotation, SendTo sendTo) {
        Set<? extends Element> elements = environment.getElementsAnnotatedWith(annotation);
        for (Element element : elements) {
            TypeElement typeElement = (TypeElement) element;
            boolean isTheType;
            isTheType = sendTo.isTheType(typeElement);
            if (!isTheType) {
                sendTo.notTheType(typeElement);
            }
        }

    }
}
